# 采集用户评论数据模块

import csv
import os
import time
from datetime import datetime

class CommentCollector:
    def __init__(self):
        # 确保data/user目录存在
        if not os.path.exists('data/user'):
            os.makedirs('data/user')
        
        # 使用固定的CSV文件名
        self.csv_file = 'data/user/xhs_comments.csv'
        
        # 定义CSV表头
        self.headers = [
            'user_id',       # 评论用户ID
            'comment_text',  # 评论内容
            'comment_time',  # 评论时间
            'collect_time',  # 数据采集时间
            'article_url',   # 评论所属文章URL
            'keyword'        # 搜索关键词
        ]
        
        # 添加processed_comments作为类属性
        self.processed_comments = set()
        
        # 当前关键词
        self.keyword = ""
        
        # 如果文件不存在，创建文件并写入表头
        if not os.path.exists(self.csv_file):
            self._create_csv()
            
    def set_keyword(self, keyword):
        """设置当前搜索关键词，但不改变文件名"""
        self.keyword = keyword
        # 不再修改CSV文件名，使用固定名称
        # 确保文件存在
        if not os.path.exists(self.csv_file):
            self._create_csv()

    def _create_csv(self):
        """创建CSV文件并写入表头"""
        with open(self.csv_file, 'w', encoding='utf-8-sig', newline='') as f:
            writer = csv.writer(f)
            writer.writerow(self.headers)
            print(f"创建评论数据文件：{self.csv_file}")

    def collect_comments(self, page):
        """采集评论数据"""
        try:
            # 定位评论区域
            comments = page.eles('.parent-comment')
            if not comments:
                print("未找到评论")
                return False
            
            comment_count = len(self.processed_comments)  # 使用类属性
            article_url = page.url  # 获取当前文章URL
            
            # 处理当前可见的评论
            for comment in comments:
                try:
                    # 获取评论内容
                    comment_element = comment.ele('.note-text')
                    if comment_element:
                        raw_comment_text = comment_element.text
                        print(f"评论内容: '{raw_comment_text}'")
                        comment_text = raw_comment_text.strip() if raw_comment_text else ""
                        # 不再过滤表情符号，保留原始文本
                    else:
                        comment_text = ""
                        print("警告: 未找到评论文本元素")
                    
                    # 如果评论为空，跳过
                    if not comment_text:
                        print("警告: 评论文本为空，跳过")
                        continue
                    
                    # 生成评论唯一标识（评论内容+文章URL）
                    comment_id = f"{comment_text}_{article_url}"
                    if comment_id in self.processed_comments:  # 使用类属性
                        continue
                    
                    # 获取评论用户ID
                    # 尝试多种XPath定位评论用户
                    user_element = comment.ele('xpath:.//div[2]/div[1]/div[1]/a')
                    if not user_element:
                        user_element = comment.ele('xpath:.//a[contains(@class, "user-name")]')
                    if not user_element:
                        user_element = comment.ele('xpath:.//a[contains(@class, "author")]')
                    
                    if user_element:
                        raw_user_id = user_element.text
                        print(f"评论用户: '{raw_user_id}'")
                        user_id = raw_user_id.strip() if raw_user_id else ""
                        # 不再过滤表情符号，保留原始文本
                    else:
                        user_id = "未知用户"
                        print("警告: 未找到评论用户元素")
                    
                    # 获取评论时间
                    time_element = comment.ele('.date')
                    if time_element:
                        # 完整保留评论时间，包括"天前"字样
                        raw_comment_time = time_element.text
                        print(f"评论时间: '{raw_comment_time}'")
                        comment_time = self._clean_comment_time(raw_comment_time)
                    else:
                        comment_time = ""
                        print("警告: 未找到评论时间元素")
                    
                    # 获取当前时间作为采集时间
                    collect_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                    
                    # 打印将要保存的数据
                    print(f"即将保存评论: 用户='{user_id}', 评论='{comment_text[:20]}...', 时间='{comment_time}'")
                    
                    # 保存数据（添加关键词）
                    self._save_to_csv([user_id, comment_text, comment_time, collect_time, article_url, self.keyword])
                    
                    self.processed_comments.add(comment_id)  # 使用类属性
                    comment_count += 1
                    print(f"已采集第{comment_count}条评论")
                    
                except Exception as e:
                    print(f"处理评论时出错: {e}")
                    continue
            
            # 如果已经采集到20条或更多，返回True
            if comment_count >= 20:
                print(f"评论采集完成，共采集{comment_count}条评论")
                return True
            
            # 如果还没采集够20条，返回False继续滚动
            return False
            
        except Exception as e:
            print(f"采集评论数据时出错: {e}")
            return False

    def _is_comment_exists(self, comment_text, article_url):
        """检查评论是否已存在于CSV文件中"""
        try:
            if not os.path.exists(self.csv_file):
                return False
                
            with open(self.csv_file, 'r', encoding='utf-8-sig') as f:
                reader = csv.reader(f)
                next(reader)  # 跳过表头
                for row in reader:
                    if len(row) >= 5 and row[1] == comment_text and row[4] == article_url:
                        return True
            return False
        except Exception:
            return False

    def _save_to_csv(self, data):
        """保存数据到CSV文件"""
        with open(self.csv_file, 'a', encoding='utf-8-sig', newline='') as f:
            writer = csv.writer(f)
            writer.writerow(data)
            
    def _filter_emoji(self, text):
        """过滤掉文本中的表情符号，保留中文和其他字符"""
        if not text:
            return ""
        
        # 使用正则表达式过滤表情符号，但保留中文字符
        import re
        # 这个模式匹配大多数Unicode表情符号，但不会匹配中文字符
        emoji_pattern = re.compile(
            "["
            "\U0001F600-\U0001F64F"  # 表情符号
            "\U0001F300-\U0001F5FF"  # 符号和象形文字
            "\U0001F680-\U0001F6FF"  # 交通和地图符号
            "\U0001F700-\U0001F77F"  # 字母符号
            "\U0001F780-\U0001F7FF"  # 几何形状
            "\U0001F800-\U0001F8FF"  # 补充箭头
            "\U0001F900-\U0001F9FF"  # 补充符号和象形文字
            "\U0001FA00-\U0001FA6F"  # 扩展符号
            "\U0001FA70-\U0001FAFF"  # 表情符号扩展
            "\U00002702-\U000027B0"  # 装饰符号
            "\U000024C2-\U0001F251" 
            "]+", flags=re.UNICODE
        )
        
        filtered_text = emoji_pattern.sub(r'', text)
        
        # 调试输出
        if filtered_text != text:
            print(f"原始文本: '{text}'")
            print(f"过滤后文本: '{filtered_text}'")
            
        return filtered_text 

    def _clean_comment_time(self, time_text):
        """处理评论时间
        如果包含地区信息，只保留地区名称
        如果不包含地区信息，则保留天数数字
        """
        if not time_text:
            return ""
            
        import re
        
        # 检查是否有地区信息 (通常是中文字符，位于"天前"等字样后面)
        # 使用正则表达式匹配"天前"、"小时前"等后面的中文字符
        location_match = re.search(r'[前|后]\s*([^\s\d]+?)$', time_text)
        if location_match:
            # 如果找到地区，只返回地区名称
            location = location_match.group(1)
            print(f"提取到地区信息: '{location}'")
            return location
        
        # 如果没有地区信息，提取数字部分
        number_match = re.search(r'(\d+)', time_text)
        if number_match:
            number = number_match.group(1)
            if "天前" in time_text:
                # 只返回数字，表示天数
                return number
            elif "小时前" in time_text:
                # 将小时转换为小数天数
                hours = int(number)
                days = round(hours / 24, 1)  # 转换为天，保留一位小数
                return str(days)
            elif "分钟前" in time_text:
                # 分钟转换为非常小的天数
                return "0"
            else:
                # 其他情况，返回原始数字
                return number
        
        # 如果没有找到数字，返回原始文本
        return time_text 